
/**
 * 接口请求
 */

import { Toast } from 'vant'

import { getJSON, postJSON } from '@/utils/request'

// const log = require('@/utils/debug.log')('request')

// 记录和显示错误
function errorLog (error) {
  // log(error)
  console.log(error)
}

// 返回错误码对应的错误消息
function errorMsg (status) {
  let message = ({
    '400': '请求错误',
    '401': '未授权，请登录',
    '403': '拒绝访问',
    '404': '请求地址出错',
    '408': '请求超时',
    '500': '服务器内部错误',
    '501': '服务未实现',
    '502': '网关错误',
    '503': '服务不可用',
    '504': '网关超时',
    '505': 'HTTP版本不受支持',
    '0': '网络异常',
    '-100': '请求超时',
    '-200': '服务异常'
  })[status]
  return message || ''
}

// 转换Error对象 记录错误日志
function errorWorker (error) {
  let e = Object.assign(new Error(), error)
  let status = e.isAxiosError ? '0' : '-200' // 默认fallback错误码
  const url = e.isAxiosError ? e.config.url : e.response.config.url

  if (e.isAxiosError) {
    e.status = e.status || e.request.status // 请求状态码
    // 界面提示的错误信息
    // 优先级为匹配错误消息 > 原始错误消息 > 默认错误消息
    e.$message = errorMsg(e.status) || e.message || errorMsg(status)
    // 根据状态码对错误消息特殊处理
    switch (e.status) {
      case 0:
        if (e.message.indexOf('timeout') !== -1) { // 请求超时
          e.$message = errorMsg(status = '-100')
        }
        break
    }
  } else {
    // 优先级为原始错误消息 > 匹配错误消息 > 默认错误消息
    e.$message = e.message || errorMsg(e.status) || errorMsg(status)
    switch (e.status) {
      case '4001':
        break
    }
  }
  // Error对象错误信息
  e.message = `[${e.status || status}] ${e.$message} ${url}`
  return e
}

/**
 * 业务错误过滤器 返回对象必须有 status 和 message 两个属性
 */
function businessErrorFilter (json) {
  if (json.errcode !== '0') {
    return {
      status: json.errcode,
      message: json.errmsg
    }
  }
}

/**
 * 跟踪接口请求的每个关键时刻
 */
export function trace (request, {
  onbefore = () => {}, // 请求开始
  onerror = () => {}, // 请求错误
  onsuccess = () => {}, // 请求成功
  onafter = () => {}, // 请求结束
  errorFilter = () => {} // 错误响应过滤器
} = {}) {
  const start = new Date().getTime()
  let spend = () => new Date().getTime() - start // 计算请求耗时

  onbefore.call(request)
  return request.then(response => {
    // 错误响应过滤
    let result = errorFilter(response.json)
    if (result) {
      let raw = Object.assign(result, {
        response,
        timestamp: spend() // 请求耗时时间戳
      })
      onerror.call(request, errorWorker(raw))
    } else {
      onsuccess.call(request, response)
    }
    return Promise.resolve(response)
  }).catch(error => {
    let response = error.response || {}
    let raw = Object.assign({}, error, {
      status: response.status, // 错误码
      message: error.message, // 错误消息
      response,
      timestamp: spend() // 请求耗时时间戳
    })
    onerror.call(request, errorWorker(raw))
    return Promise.reject(error)
  }).finally(() => {
    onafter.call(request)
  })
}

let loadingTimer // 关闭loading定时器
let loadingToast

export function enhance (request, {
  isLog = true, // 是否记录日志
  isShowError = true, // 是否提示错误消息
  loadingOptions, // loading自定义配置项 true为默认loading false为不使用loading
  errorFilter = businessErrorFilter // 错误响应过滤器
} = {}) {
  if (loadingOptions) {
    clearTimeout(loadingTimer)
    // loading加载中
    if (loadingToast == null) {
      loadingToast = Toast.loading(Object.assign({
        message: '',
        loadingType: 'circular',
        forbidClick: true,
        duration: 0
      }, loadingOptions))
    }
  }
  return trace(request, {
    onerror (e) {
      if (isLog) {
        errorLog(e) // 记录错误日志
      }
      if (isShowError) {
        // 这里写展示错误消息逻辑
      }
    },
    onafter () {
      loadingTimer = setTimeout(() => {
        // 关闭loading加载中
        if (loadingToast) {
          loadingToast.clear()
          loadingToast = null
        }
      }, 250)
    },
    errorFilter
  })
}

/**
 * 链式请求方法
 * Usage:
 *   import { Request } from '@app/utils/request.enhance'
 *   Request.get(url, data)
 *   Request.post(url, data)
 *   Request(options).post(url, data, config)
 */
export function Request (options = {}) {
  let handler = {
    get: (url, data, config) => enhance(getJSON(url, data, config), options),
    post: (url, data, config) => enhance(postJSON(url, data, config), options),
    loading (loadingOptions) {
      Object.assign(options, {
        // 将loading配置项并入options配置 不存在则使用默认loading
        loadingOptions: loadingOptions || true
      })
      return this
    }
  }
  return handler
}
Object.assign(Request, {
  get () {
    return Request().get.apply(null, arguments)
  },
  post () {
    return Request().post.apply(null, arguments)
  },
  loading (loadingOptions) {
    return Request().loading(loadingOptions)
  }
})
